﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;

using SimbaRGVMonitor.Models;

namespace SimbaRGVMonitor.Views.Components
{
    partial class MonitorScheduleCanvas
    {
        internal void AddPath(Path path)
        {
            PathItem pathItem;
            if (TryCreatePathItem(path, out pathItem))
            {
                var pathNo = path.PathNo;
                pathItems.Add(pathNo, pathItem);
                canvas.Children.Add(pathItem);
            }

            SetScrollMinSize();
        }

        internal void UpdatePath(Path newPath)
        {
            PathItem newPathItem;
            var pathNo = newPath.PathNo;
            var pathItem = pathItems[pathNo];
            if (TryCreatePathItem(newPath, out newPathItem) == false)
            {
                return;
            }

            if (pathItem.StartPoint != newPathItem.StartPoint ||
                pathItem.EndPoint != newPathItem.EndPoint ||
                pathItem.ControlPoint != newPathItem.ControlPoint)
            {
                pathItems.Remove(pathNo);
                canvas.Children.Remove(pathItem);

                pathItems.Add(pathNo, newPathItem);
                canvas.Children.Add(newPathItem);

                SetScrollMinSize();
            }
        }

        internal void RemovePath(string pathNo)
        {
            var item = pathItems[pathNo];
            pathItems.Remove(pathNo);
            canvas.Children.Remove(item);

            SetScrollMinSize();
        }

        internal void ResetPaths(Path[] paths)
        {
            var pathItemKeys = pathItems.Keys.OrderBy(item => item).ToArray();
            var pathKeys = paths.Select(item => item.PathNo).OrderBy(item => item).ToArray();

            var addKeys = pathKeys.Except(pathItemKeys);
            var updateKeys = pathKeys.Intersect(pathItemKeys).ToArray();
            var removeKeys = pathItemKeys.Except(pathKeys).ToArray();

            foreach (var key in removeKeys)
            {
                RemovePath(key);
            }

            foreach (var key in addKeys)
            {
                var path = paths.FirstOrDefault(item => item.PathNo == key);
                AddPath(path);
            }

            foreach (var key in updateKeys)
            {
                var newPath = paths.FirstOrDefault(item => item.PathNo == key);
                UpdatePath(newPath);
            }
        }

        private bool TryCreatePathItem(Path path, out PathItem pathItem)
        {
            var endVertex = path.EndVertex;
            var startVertex = path.StartVertex;
            var reversePathNo = string.Format("{0}-{1}", endVertex, startVertex);
            if (pathItems.ContainsKey(reversePathNo))
            {
                pathItem = null;
                return false;
            }

            Point endPoint;
            Point startPoint;
            Point controlPoint;
            if(CalculatePathPoints(path, out startPoint, out endPoint, out controlPoint) == false)
            {
                pathItem = null;
                return false;
            }

            pathItem =
                new PathItem
                {
                    StartPoint = startPoint,
                    EndPoint = endPoint,
                    ControlPoint = controlPoint,
                    ArrowLength = 0,
                    Stroke = Brushes.Black,
                    StrokeThickness = 0.5,
                    Visibility = Visibility.Visible
                };

            return true;
        }

        private bool CalculatePathPoints(Path path, out Point startPoint, out Point endPoint, out Point controlPoint)
        {
            VertexItem endVertexItem;
            VertexItem startVertexItem;
            var endVertex = path.EndVertex;
            var startVertex = path.StartVertex;
            if (vertexItems.TryGetValue(endVertex, out endVertexItem) == false ||
                vertexItems.TryGetValue(startVertex, out startVertexItem) == false)
            {
                startPoint = new Point();
                endPoint = new Point();
                controlPoint = new Point();
                return false;
            }

            if (endVertexItem.TryGetActualCenter(out endPoint) == false ||
                startVertexItem.TryGetActualCenter(out startPoint) == false)
            {
                startPoint = new Point();
                endPoint = new Point();
                controlPoint = new Point();
                return false;
            }

            var dist = Math.Sqrt(Math.Pow(startPoint.X - endPoint.X, 2) + Math.Pow(startPoint.Y - endPoint.Y, 2)) / 1.5;
            controlPoint =
                new Point
                {
                    X = Math.Round(endPoint.X - dist * Math.Cos(path.PathDestA * Math.PI / 180), 2),
                    Y = Math.Round(endPoint.Y + dist * Math.Sin(path.PathDestA * Math.PI / 180), 2)
                };
            return true;
        }
    }
}
